home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 1.iso / DEMON / RISCOS2 / TCP_131S.ARC / c / nntpcli < prev    next >
Text File  |  1994-03-10  |  31KB  |  1,354 lines

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <stdarg.h>
  4. #include <time.h>
  5. #include <ctype.h>
  6. #include <string.h>
  7. #include "kernel.h"
  8. #include "global.h"
  9. #include "timer.h"
  10. #include "cmdparse.h"
  11. #include "netuser.h"
  12. #include "nntp.h"
  13. #include "smtp.h"
  14. #include "domain.h"
  15. #include "misc.h"
  16. #include "tcp.h"
  17. #include "mbuf.h"
  18. #include "arc.h"
  19. #include "Terminal.h"
  20.  
  21. #include "flex.h"
  22. #include "os.h"
  23. #include "swis.h"
  24.  
  25. #include "Reader.h"
  26.  
  27. #define NNTPMAXLEN      512
  28. #define BATCH_MAX        16
  29.  
  30. #define ID_LEN 28
  31.  
  32. #define NN_CMD             0
  33. #define NN_DAT          1
  34. #define NN_RDY          2
  35.  
  36. #define NN_OPEN          1
  37. #define NN_NEWG          2
  38. #define NN_NEWN          3
  39. #define NN_GETN          4
  40. #define NN_BYNUM         5
  41. #define NN_SELG         6
  42. #define NN_GNUM         7
  43. #define NN_NEXT_STAGE          8
  44. #define NN_POST         9
  45. #define NN_PREARTSEND_STAGE 10
  46. #define NN_POSTARTSEND_STAGE 11
  47. #define NN_QUIT          12
  48.  
  49.  
  50. static struct nntpservers {
  51.   struct timer nntpcli_t;
  52.   char *name;
  53.   char *abbr;
  54.   char *temp;
  55.   int lowtime, hightime;  /* for connect window */
  56.   struct nntpservers *next;
  57.   struct tcb *tcb;        /* tcp task control buffer */
  58.   int32   ipdest;         /* address of forwarding system */
  59.   char    state;          /* state machine placeholder */
  60.   char    stage;          /* another state machine placeholder */
  61.   char    buf[LINELEN];   /* Input buffer */
  62.   char    cnt;            /* Length of input buffer */
  63.   FILE    *input1;
  64.   FILE    *input0;
  65.   FILE    *output;
  66.   FILE    *id_file;
  67.   BOOL    RS_Hist;
  68.   int     dup_method;
  69.   int     batch;
  70.   char    now[20];
  71.   char    date[20];
  72.   char    hour[20];
  73.   long    file_len;
  74.   int     msg_tot;
  75.   char    goodrcpt;       /* are any of the rcpt ok */
  76.   char    cts;            /* clear to send state indication */
  77.   int     rcpts;          /* number of unacked rcpt commands */
  78.   struct  list    *errlog;
  79.   char (*got_table)[ID_LEN];
  80.   int     lastread;
  81.   struct  directory *topost;
  82.   long    rnewspos;
  83.   Terminal *window;
  84. };
  85.  
  86. struct directory
  87. {
  88.   struct directory *next;
  89.   char *name;
  90. };
  91.  
  92. #define NULLNNTP        (struct nntpservers *)NULL
  93.  
  94. struct nntpservers *Nntpservers = NULLNNTP;
  95.  
  96. static void nntptick(void *);
  97. static void make_got_table(int, void *);
  98. static int doadds(int, char **);
  99. static int dobatch(int, char **);
  100. static int dodirect(int, char **);
  101. static int dodrops(int, char **);
  102. static int dodups(int, char **);
  103. static int dofudge(int, char **);
  104. static int dokicks(int, char **);
  105. static int dolists(int, char **);
  106. static int dostop(int, char **);
  107. static int donntptrace(int, char **);
  108. static void quit(struct nntpservers *, BOOL);
  109. static void nntp_rec(struct tcb *, int16);
  110. static void nntp_cts(struct tcb *, int16);
  111. static void nntp_state(struct tcb *, char, char);
  112. static void sendit(struct nntpservers *, char *, ...);
  113.  
  114. extern int lport;                     /* local port placeholder */
  115.  
  116. static int   nntptrace = 0;                  /* used for trace level */
  117.  
  118. static char quitcmd[] = "QUIT\r\n";
  119.  
  120. static char *duptab[] = { "auto", "ReaderS", "history" };
  121.  
  122. static int dup_method = 0;
  123. static int fudge      = 0;
  124. static int batch      = 4;
  125. static BOOL direct    = FALSE;
  126.  
  127. struct cmds nntpcmds[] = {
  128.   "addserver",  doadds,      4, "nntp addserver <name> <time> <abbr>", NULLCHAR,
  129.   "batch",      dobatch,     1, "nntp batch <n>",                      NULLCHAR,
  130.   "direct",     dodirect,    1, "nntp direct <on|off>",                NULLCHAR,
  131.   "dropserver", dodrops,     2, "nntp dropserver <name|abbr>",         NULLCHAR,
  132.   "duplicates", dodups,      1, "nntp duplicates [<auto|readers|history>]",         NULLCHAR,
  133.   "fudge",      dofudge,     1, "nntp fudge [<time>]",                   NULLCHAR,
  134.   "kick",       dokicks,     2, "nntp kick <name|abbr>",               NULLCHAR,
  135.   "listserver", dolists,     0, NULLCHAR,                              NULLCHAR,
  136.   "stop",       dostop,      0, "nntp stop <name|abbr>",               NULLCHAR,
  137.   "trace",      donntptrace, 0, NULLCHAR,                              NULLCHAR,
  138.   NULLCHAR,
  139. };
  140.  
  141. int donntp(int argc, char **argv)
  142. {
  143.   return subcmd(nntpcmds,argc,argv);
  144. }
  145.  
  146. static int donntptrace(int argc, char **argv)
  147. {
  148.   if (argc < 2)
  149.     cwprintf(NULL, "%d\r\n", nntptrace);
  150.   else 
  151.     nntptrace = atoi(argv[1]);
  152.   return 0;
  153. }
  154.  
  155. static int dofudge(int argc, char *argv[])
  156. {
  157.   if (argc < 2)
  158.   {
  159.     cwprintf(NULL, "NNTP - Fudge factor %d\n", fudge);
  160.   }
  161.   else
  162.   {
  163.     fudge = abs(atoi(argv[1]));
  164.   }
  165.   return 0;
  166. }
  167.  
  168. static int dobatch(int argc, char *argv[])
  169. {
  170.   if (argc < 2)
  171.   {
  172.     cwprintf(NULL, "NNTP - Batch %d\n", batch);
  173.   }
  174.   else if (atoi(argv[1]) > 0 && atoi(argv[1]) < BATCH_MAX)
  175.   {
  176.     fudge = abs(atoi(argv[1]));
  177.   }
  178.   return 0;
  179. }
  180.  
  181. static int dodirect(int argc, char *argv[])
  182. {
  183.   if (argc < 2)
  184.   {
  185.     cwprintf(NULL, "NNTP - direct %s\n", direct ? "on" : "off");
  186.   }
  187.   else if (stricmp(argv[1], "on") == 0)
  188.   {
  189.     direct = TRUE;
  190.   }
  191.   else
  192.   {
  193.     direct = FALSE;
  194.   }
  195.   return 0;
  196. }
  197.  
  198. static int dodups(int argc, char *argv[])
  199. {
  200.   int loop;
  201.   
  202.   if (argc < 2)
  203.   {
  204.     cwprintf(NULL, "%s\n", duptab[dup_method]);
  205.   }
  206.   else
  207.   {
  208.     for (loop = 0; loop < 3; loop++)
  209.     {
  210.       if (strnicmp(argv[1], duptab[loop], MIN(strlen(argv[1]), strlen(duptab[loop]))) == NULL)
  211.         break;
  212.     }
  213.     if (loop == 3)
  214.       loop = 0;
  215.     dup_method = loop;
  216.   }
  217.   return 0;
  218. }
  219.  
  220. static int doadds(int argc, char *argv[])
  221. {
  222.   BOOL auto_fail = FALSE;
  223.   char buffer[256];
  224.   struct nntpservers *np;
  225.   
  226.   for(np = Nntpservers; np != NULLNNTP; np = np->next)
  227.     if (stricmp(np->name, argv[1]) == 0)
  228.       break;
  229.   if (np == NULLNNTP)
  230.   {
  231.     np = (struct nntpservers *) calloc(1, sizeof(struct nntpservers));
  232.     np->name    = strdup(argv[1]);
  233.     np->abbr    = strdup(argv[3]);
  234.     np->ipdest  = resolve(argv[1]);
  235.     np->next    = Nntpservers;
  236.     Nntpservers = np;
  237.     np->lowtime = np->hightime = -1;
  238.     np->nntpcli_t.func = nntptick;  /* what to call on timeout */
  239.     np->nntpcli_t.arg  = (void *)np;
  240.     np->state      = NN_RDY;
  241.     np->stage      = NN_OPEN;
  242.     np->input0     = NULL;
  243.     np->input1     = NULL;
  244.     np->output     = NULL;
  245.     np->got_table  = NULL;
  246.     np->RS_Hist    = FALSE;
  247.     np->dup_method = dup_method;
  248.     if (dup_method == 0 || dup_method == 1)
  249.     {
  250.       sprintf(buffer, "<ReadBack$Dir>.%sMail", np->abbr);
  251.       if (np->id_file = fopen(buffer, "r"), np->id_file != NULL)
  252.       {
  253.         fclose(np->id_file);
  254.         np->id_file = NULL;
  255.         np->RS_Hist = TRUE;
  256.       }
  257.       else
  258.       {
  259.         auto_fail = TRUE;
  260.       }
  261.     }
  262.     if ((dup_method == 0 && auto_fail) || dup_method == 2)
  263.     {
  264.       sprintf(buffer, "<NNTP$Dir>.%sHist", np->abbr);
  265.       if (np->id_file = fopen(buffer, "r"), np->id_file == NULL)
  266.       {
  267.         np->id_file = fopen(buffer, "w");
  268.       }
  269.       fclose(np->id_file);
  270.       np->id_file = NULL;
  271.       np->RS_Hist = FALSE;    
  272.     }
  273.     make_got_table(0, (void *)np);
  274.   }
  275.   if (argc > 4)
  276.   {
  277.     int i;
  278.     for (i = 4; i < argc; ++i)
  279.     {
  280.       if (isdigit(*argv[i]))
  281.       {
  282.         int lh, ll, hh, hl;
  283.         sscanf(argv[i], "%d:%d-%d:%d", &lh, &ll, &hh, &hl);
  284.         np->lowtime = lh * 100 + ll;
  285.         np->hightime = hh * 100 + hl;
  286.       } 
  287.     }
  288.   }
  289.   /* set timer duration */
  290.   set_timer(&(np->nntpcli_t), atol(argv[2]) * 1000L);
  291.   start_timer(&(np->nntpcli_t));
  292.  
  293.   return 0;
  294. }
  295.  
  296. static int dodrops(int argc, char *argv[])
  297. {
  298.   struct nntpservers *np, *npprev = NULLNNTP;
  299.   for(np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
  300.   {
  301.     if (stricmp(np->name, argv[1]) == 0 || stricmp(np->abbr, argv[1]) == 0)
  302.     {
  303.       stop_timer(&np->nntpcli_t);
  304.       free(np->name);
  305.       free(np->abbr);
  306.       if (np->input1 != NULL)
  307.         fclose(np->input1);
  308.       if (np->input0 != NULL)
  309.         fclose(np->input0);
  310.       if (np->output != NULL)
  311.         fclose(np->output);
  312.       if (np->got_table != NULL)
  313.       {
  314.         flex_free((flex_ptr) &(np->got_table));
  315.         np->got_table = NULL;
  316.         np->msg_tot = 0;
  317.       }
  318.       if(npprev != NULLNNTP)
  319.         npprev->next = np->next;
  320.       else
  321.           Nntpservers = np->next;
  322.       free((char *)np);
  323.       return 0;
  324.     }
  325.   }
  326.   cwprintf(NULL, "NNTP - No such server enabled.\r\n");
  327.   return 0;
  328. }
  329.  
  330. static int dokicks(int argc, char *argv[])
  331. {
  332.   struct nntpservers *np;
  333.   for (np = Nntpservers; np != NULLNNTP; np = np->next)
  334.   {
  335.     if (stricmp(np->name, argv[1]) == 0 || stricmp(np->abbr, argv[1]) == 0)
  336.     {
  337.       /* If the timer is not running, the timeout function has
  338.          already been called and we don't want to call it again. */
  339.       if (run_timer(&np->nntpcli_t))
  340.       {
  341.         np->state = NN_CMD;
  342.         np->stage = NN_OPEN;
  343.         stop_timer(&np->nntpcli_t);
  344.         nntptick((void *)np);
  345.       }
  346.       else
  347.       {
  348.         cwprintf(NULL, "NNTP - Can't restart\r\n");
  349.       }
  350.       return 0;
  351.     }
  352.   }
  353.   cwprintf(NULL, "NNTP - No such server enabled.\r\n");
  354.   return 0;
  355. }
  356.  
  357. static int dostop(int argc, char *argv[])
  358. {
  359.   struct nntpservers *np;
  360.   for(np = Nntpservers; np != NULLNNTP; np = np->next)
  361.   {
  362.     if (stricmp(np->name, argv[1]) == 0 || stricmp(np->abbr, argv[1]) == 0)
  363.     {
  364.       quit(np, FALSE);
  365.       start_timer(&np->nntpcli_t);
  366.       return 0;
  367.     }
  368.   }
  369.   cwprintf(NULL, "NNTP - No such server enabled.\r\n");
  370.   return 0;
  371. }
  372.  
  373. static struct directory *make_dir(char *dir)
  374. {
  375.   struct directory *list=NULL, *entry;
  376.   char name[256];
  377.  
  378.   for (filedir(dir, 0, name); *name; filedir(dir, 1, name))
  379.   {
  380.     if (entry = malloc(sizeof(struct directory)), entry == 0)
  381.       break;
  382.     entry->name = strdup(name);
  383.     entry->next = list;
  384.     list = entry;
  385.   }
  386.   return list;
  387. }
  388.  
  389. static void kill_dir(struct directory *dir)
  390. {
  391.   struct directory *entry;
  392.  
  393.   for (entry = dir; entry; )
  394.   {
  395.     dir = entry->next;
  396.     free(entry->name);
  397.     free(entry);
  398.     entry = dir;
  399.   }
  400. }
  401.  
  402. static int id_sort(const void *obj1, const void *obj2)
  403. {
  404.   return(strcmp((char *) obj1, (char *) obj2));
  405. }
  406.  
  407. static void make_got_table(int at, void *handle)
  408. {
  409.   char buffer[256];
  410.   int loop;
  411.   message_data temp;
  412.   struct nntpservers *cb;
  413.  
  414.   at = at;
  415.   cb = (struct nntpservers *) handle;
  416.  
  417.   if (cb->got_table == NULL)
  418.   {
  419.     cb->msg_tot = 0;
  420.     if (!flex_alloc((flex_ptr) &(cb->got_table), ID_LEN * 21))
  421.       return;
  422.   }
  423.  
  424.   if (cb->id_file == NULL)
  425.   {
  426.     if (cb->RS_Hist)
  427.     {
  428.       sprintf(buffer, "<ReadBack$Dir>.%sMail", cb->abbr);
  429.       if (cb->id_file = fopen(buffer, "r"), cb->id_file != NULL)
  430.       {
  431.         fseek(cb->id_file, 0, SEEK_END);
  432.         cb->file_len = ftell(cb->id_file);
  433.         fseek(cb->id_file, 9, SEEK_SET);
  434.       }
  435.     }
  436.     else if (cb->dup_method != 1)
  437.     {
  438.       sprintf(buffer, "<NNTP$Dir>.%sHist", cb->abbr);
  439.       if (cb->id_file = fopen(buffer, "r+"), cb->id_file != NULL)
  440.       {
  441.         fseek(cb->id_file, 0, SEEK_END);
  442.         cb->file_len = ftell(cb->id_file);
  443.         fseek(cb->id_file, 0, SEEK_SET);
  444.       }
  445.     }
  446.   }
  447.  
  448.   if (cb->id_file && ftell(cb->id_file) < cb->file_len)
  449.   {
  450.     if (flex_extend((flex_ptr) &(cb->got_table), (cb->msg_tot + 21) * ID_LEN))
  451.     {
  452.       for (loop = 0; loop < 20 && ftell(cb->id_file) < cb->file_len; loop++)
  453.       {
  454.         if (cb->RS_Hist)
  455.         {
  456.           fread(&temp, sizeof(message_data), 1, cb->id_file);
  457.           fgets(buffer, 250, cb->id_file);
  458.         }
  459.         fgets(buffer, 250, cb->id_file);
  460.         strncpy((cb->got_table)[cb->msg_tot], (buffer[0] == '<' ? &buffer[1] : buffer), ID_LEN);
  461.         (cb->got_table)[cb->msg_tot][ID_LEN - 1] = '\0';
  462.         rip((cb->got_table)[cb->msg_tot]);
  463.         if (cb->RS_Hist)
  464.           fseek(cb->id_file, temp.comp_bytes + 4L, SEEK_CUR);
  465.         cb->msg_tot++;
  466.       }
  467.       qsort(cb->got_table, cb->msg_tot, ID_LEN, id_sort);
  468.     }
  469.     else
  470.     {
  471.       if (nntptrace)
  472.         cwprintf(NULL, "NNTP - No room for dup check table\r\n");
  473.     }
  474.   }
  475.   else
  476.   {
  477.     if (cb->RS_Hist && cb->id_file)
  478.     {
  479.       fclose(cb->id_file);
  480.       cb->id_file = NULL;
  481.     }
  482.     if (nntptrace > 2)
  483.       cwprintf(NULL, "NNTP - Loaded %d IDs\r\n", cb->msg_tot);
  484.     return;
  485.   }
  486.   alarm_set(alarm_timenow() + 2, make_got_table, (void *)cb);
  487. }
  488.  
  489. static void nntptick(void *tp)
  490. {
  491.   char title[256];
  492.   struct socket lsocket, fsocket;
  493.   register struct nntpservers *cb;
  494.  
  495.   cb = (struct nntpservers *) tp;
  496.   if (cb == NULL) 
  497.     return;
  498.   if (cb->state != NN_CMD)
  499.     return;
  500.  
  501.   sprintf(title, "NNTP - %s", cb->name);
  502.   cb->window = Window_Open(NULL, title, term_NO_INPUT | term_DONT_DESTROY);
  503.   /* setup the socket */
  504.   fsocket.address = cb->ipdest;
  505.   fsocket.port    = NNTP_PORT;
  506.   lsocket.address = ip_addr;      /* our ip address */
  507.   lsocket.port    = lport++;      /* next unused port */
  508.  
  509.   if (nntptrace)
  510.   {
  511.     cwprintf(cb->window, "NNTP daemon entered\r\n",inet_ntoa(fsocket.address));
  512.   }
  513.  
  514.   if (nntptrace > 1)
  515.   {
  516.     cwprintf(cb->window, "NNTP trying Connection to %s\r\n",inet_ntoa(fsocket.address));
  517.   }
  518.   stop_timer(&cb->nntpcli_t);
  519.  
  520.   cb->batch  = 0;
  521.   cb->now[0] = 3;
  522.   os_word(14, cb->now);
  523.   time_adj(cb->now, -(fudge));
  524.   if (cb->RS_Hist == FALSE) 
  525.   {
  526.     long offset;
  527.  
  528.     if (cb->id_file == NULL)
  529.     {
  530.       sprintf(title, "<NNTP$Dir>.%sHist", cb->abbr);
  531.       cb->id_file = fopen(title, "a+");
  532.       offset = 0;
  533.     }
  534.     else
  535.     {
  536.       offset = ftell(cb->id_file);
  537.     }
  538.     fseek(cb->id_file, 0, SEEK_END);
  539.     cb->file_len = ftell(cb->id_file);
  540.     os_swi4(OS_ConvertDateAndTime, (int) cb->now, (int) title, 30, (int) "%YR%MN%DY %24%MI%SE");
  541.     fprintf(cb->id_file, "# %s\n", title);
  542.     fseek(cb->id_file, offset, SEEK_SET);
  543.   }
  544.  
  545.   /* open nntp connection */
  546.   cb->stage = NN_OPEN;      /* init stage placeholder */
  547.   cb->state = NN_CMD;       /* init state placeholder */
  548.   cb->tcb = open_tcp(&lsocket, &fsocket, TCP_ACTIVE, tcp_window, (void(*)())nntp_rec, (void(*)())nntp_cts, (void(*)())nntp_state, 0, (char *)cb);
  549.   cb->tcb->user = (char *)cb;     /* Upward pointer */
  550. }
  551.  
  552. static BOOL have_we(char *line, struct nntpservers *cb)
  553. {
  554.   BOOL gotmem = TRUE;
  555.   char buffer[LINELEN];
  556.  
  557.   rip(line);
  558.   strcpy(buffer, &line[1]);
  559.  
  560.   buffer[ID_LEN - 1] = '\0';
  561.  
  562.   if (bsearch(buffer, cb->got_table, cb->msg_tot, ID_LEN, id_sort) != NULL)
  563.   {
  564.     if (nntptrace > 2)
  565.       cwprintf(cb->window, "NNTP - Already got %s\r\n", line);
  566.     return(TRUE);
  567.   }
  568.   if (cb->msg_tot % 20 == 0)
  569.     gotmem = flex_extend((flex_ptr) &(cb->got_table), (cb->msg_tot + 21) * ID_LEN);
  570.   if (gotmem)
  571.   {
  572.     strncpy((cb->got_table)[cb->msg_tot], buffer, ID_LEN);
  573.     (cb->got_table)[cb->msg_tot][ID_LEN - 1] = '\0';
  574.     rip((cb->got_table)[cb->msg_tot]);
  575.     cb->msg_tot++;
  576.     qsort(cb->got_table, cb->msg_tot, ID_LEN, id_sort);
  577.   }
  578.   if (cb->RS_Hist == FALSE && cb->id_file)
  579.   {
  580.     long offset = ftell(cb->id_file);
  581.     
  582.     fseek(cb->id_file, 0, SEEK_END);
  583.     fprintf(cb->id_file, "%s\n", line);
  584.     fseek(cb->id_file, offset, SEEK_SET);
  585.   }
  586.   return(FALSE);
  587. }
  588.  
  589. BOOL nntp_select(struct nntpservers *cb)
  590. {
  591.   BOOL ret = FALSE;
  592.   char buffer[256];
  593.  
  594.   if( !cb->input0 )
  595.   {
  596.     sprintf(buffer, "<NNTP$Dir>.%sNGroup", cb->abbr);
  597.     cb->input0 = fopen(buffer, "rb+");
  598.   }
  599.  
  600.   if (cb->input0)
  601.   {
  602.     if (fgets(buffer, LINELEN - 1, cb->input0) != 0 && strlen(buffer) > 12)
  603.     {
  604.       strtok(buffer, " \n");
  605.       cb->lastread = atoi(strtok(NULL, " \n"));
  606.  
  607.       if (nntptrace > 1)
  608.       {
  609.         cwprintf(cb->window, "NNTP - Selecting group %s\r\n", buffer);
  610.       }
  611.       rip(buffer);
  612.       sendit(cb,"GROUP %s\r\n", buffer);
  613.       cb->stage = NN_SELG;
  614.       cb->state = NN_CMD;
  615.       ret = TRUE;
  616.     }
  617.     else
  618.     {
  619.       fclose(cb->input0);
  620.       cb->input0 = NULL;
  621.       cb->stage  = NN_QUIT;
  622.       cb->state  = NN_RDY;
  623.     }
  624.   }
  625.   return(ret);
  626. }
  627.  
  628. static BOOL nntp_getnews(register struct nntpservers *cb)
  629. {
  630.   BOOL ret = FALSE;
  631.   char buffer[256], line[NNTPMAXLEN + 1];
  632.  
  633.   if (cb->input1 == NULL && cb->batch == 0)
  634.   {
  635.     sprintf(buffer, "<NNTP$Dir>.%sNew", cb->abbr);
  636.     cb->input1 = fopen(buffer, "r");
  637.   }
  638.  
  639.   if (cb->input1 != NULL)
  640.   {
  641.     while (cb->batch < batch && cb->input1 != NULL && !feof(cb->input1))
  642.     {
  643.       while(fgets(line, LINELEN - 1, cb->input1) != NULL && have_we(line, cb))
  644.         ;
  645.       if (feof(cb->input1))
  646.       {
  647.         fclose(cb->input1);
  648.         cb->input1 = NULL;
  649.         sprintf(buffer, "<NNTP$Dir>.%sNew", cb->abbr);
  650.         remove(buffer);
  651.       }
  652.       else
  653.       {
  654.         rip(line);
  655.         sendit(cb, "ARTICLE %s\r\n", line);
  656.         cb->stage = NN_GETN;
  657.         cb->state = NN_CMD;
  658.         (cb->batch)++;
  659.         if (nntptrace > 2)
  660.         {
  661.           cwprintf(cb->window, "NNTP - Asking for article %s\r\n", line);
  662.         }
  663.       }
  664.     }
  665.   }
  666.   if (cb->batch > 0)
  667.   {
  668.     cb->stage = NN_GETN;
  669.     cb->state = NN_CMD;
  670.     ret = TRUE;
  671.   }
  672.   else
  673.   {
  674.     cb->stage = NN_NEWN;
  675.     cb->state = NN_RDY;
  676.     ret = FALSE;
  677.   }
  678.   return(ret);
  679. }
  680.  
  681. static BOOL nntp_newnews(register struct nntpservers *cb)
  682. {
  683.   BOOL ret = FALSE;
  684.   char buffer[256], line[NNTPMAXLEN + 1];
  685.  
  686.   if (cb->input0 == NULL)
  687.   {
  688.     sprintf(buffer, "<NNTP$Dir>.%sGroup", cb->abbr);
  689.     cb->input0 = fopen(buffer, "r");
  690.   }
  691.  
  692.   if (cb->input0 != NULL)
  693.   {
  694.     if (fgets(line, NNTPMAXLEN - 25, cb->input0) != NULL && isalpha(*line))
  695.     {
  696.       rip(line);
  697.       sendit(cb, "NEWNEWS %s %s %s GMT\r\n", line, cb->date, cb->hour);
  698.       cb->stage = NN_NEWN;
  699.       cb->state = NN_CMD;
  700.  
  701.       if (nntptrace > 1)
  702.         cwprintf(cb->window, "NNTP - Checking group %s\r\n", line);
  703.       ret = TRUE;
  704.     }
  705.     else
  706.     {
  707.       FILE *temp;
  708.       
  709.       fclose(cb->input0);
  710.       cb->input0 = NULL;
  711.  
  712.       sprintf(buffer, "<NNTP$Dir>.%sLast", cb->abbr);
  713.       if (temp = fopen(buffer, "w"), temp != NULL)
  714.       {
  715.         os_swi4(OS_ConvertDateAndTime, (int) cb->now, (int) cb->date, 10, (int) "%YR%MN%DY");
  716.         os_swi4(OS_ConvertDateAndTime, (int) cb->now, (int) cb->hour, 10, (int) "%24%MI%SE");
  717.         fprintf(temp, "%s\n%s\n", cb->date, cb->hour);
  718.         fclose(temp);
  719.         ret = FALSE;
  720.       }
  721.       cb->state = NN_RDY;
  722.       cb->stage = NN_QUIT /* NN_BYNUM */;
  723.     }
  724.   }
  725.   else
  726.   {
  727.     cb->state = NN_RDY;
  728.     cb->stage = NN_QUIT /* NN_BYNUM */;
  729.   }
  730.   return(ret);
  731. }
  732.  
  733. static BOOL nntp_newgroups(register struct nntpservers *cb)
  734. {
  735.   BOOL ret = FALSE;
  736.   char buffer[256];
  737.   FILE *temp;
  738.   
  739.   sprintf(buffer, "<NNTP$Dir>.%sLast", cb->abbr);
  740.   if (temp = fopen(buffer, "r"), temp != NULL)
  741.   {
  742.     if (fgets(cb->date, 8, temp) == NULL)
  743.       os_swi4(OS_ConvertDateAndTime, (int) cb->now, (int) cb->date, 10, (int) "%YR%MN%DY");
  744.     if (fgets(cb->hour, 8, temp) == NULL)
  745.       strcpy(cb->hour, "000001");
  746.     fclose(temp);
  747.   }
  748.   else
  749.   {
  750.     os_swi4(OS_ConvertDateAndTime, (int) cb->now, (int) cb->date, 10, (int) "%YR%MN%DY");
  751.     strcpy(cb->hour, "000001");
  752.   }
  753.   rip(cb->date);
  754.   rip(cb->hour);
  755.   sendit(cb, "NEWGROUPS %s %s GMT\r\n", cb->date, cb->hour);
  756.   ret = TRUE;
  757.   if (nntptrace > 1)
  758.     cwprintf(cb->window, "NNTP - Checking for new groups since last call\r\n");
  759.  
  760.   return(ret);
  761. }
  762.  
  763. static BOOL nntp_next_command(register struct nntpservers *cb)
  764. {
  765.   BOOL ret = FALSE;
  766.   
  767.   switch(cb->stage)
  768.   {
  769.   case NN_POST:
  770.     sendit(cb, "POST\r\n");
  771.     cb->state = NN_CMD;
  772.     cb->stage = NN_POST;
  773.     ret = TRUE;
  774.     break;
  775.  
  776.   case NN_NEWG:
  777.     if (nntp_newgroups(cb))
  778.     {
  779.       cb->state = NN_CMD;
  780.       ret = TRUE;
  781.     }
  782.     else
  783.     {
  784.       cb->state = NN_RDY;
  785.       cb->stage = NN_NEWN;
  786.       ret = FALSE;
  787.     }
  788.     break;
  789.  
  790.   case NN_NEWN:
  791.     if (nntp_newnews(cb))
  792.     {
  793.       cb->state = NN_CMD;
  794.       ret = TRUE;
  795.     }
  796.     else
  797.     {
  798.       cb->state = NN_RDY;
  799.       cb->stage = NN_QUIT /* NN_SELG */;
  800.       ret = FALSE;
  801.     }
  802.     break;
  803.  
  804.   case NN_GETN:
  805.     if (nntp_getnews(cb))
  806.     {
  807.       cb->state = NN_CMD;
  808.       ret = TRUE;
  809.     }
  810.     else
  811.     {
  812.       cb->state = NN_RDY;
  813.       cb->stage = NN_NEWN;
  814.       ret = FALSE;
  815.     }
  816.     break;
  817.  
  818.   case NN_SELG:
  819.     if (nntp_select(cb))
  820.     {
  821.       cb->state = NN_CMD;
  822.       ret = TRUE;
  823.     }
  824.     else
  825.     {
  826.       cb->state = NN_RDY;
  827.       cb->stage = NN_QUIT;
  828.       ret = FALSE;
  829.     }
  830.     break;
  831.  
  832.   default:
  833.     quit(cb, TRUE);
  834.     ret = TRUE;
  835.     break;
  836.   }
  837.   return(ret); 
  838. }
  839.  
  840. static void save_news(register struct nntpservers *cb)
  841. {
  842.   if (direct)
  843.   {
  844.     char buffer[256];
  845.     FILE *out;
  846.     long item_len;
  847.     
  848.     sprintf(buffer, "<Mail$Dir>.Folder.%sNews", cb->abbr);
  849.     out = fopen(buffer, "r+");
  850.     fseek(out, 0, SEEK_END);
  851.     item_len = ftell(out);
  852.     fseek(out, cb->rnewspos, SEEK_SET);
  853.  
  854.     fprintf(out, "#! rnews %07ld\n", item_len - cb->rnewspos - 17L);
  855.     fseek(out, 0, SEEK_END);
  856.     fclose(out);
  857.   }
  858.   else
  859.   {
  860.     char line[NNTPMAXLEN + 1], buffer[256];
  861.     FILE *temp, *out;
  862.     long item_len;
  863.     
  864.     sprintf(buffer, "<Mail$Dir>.Folder.%sNews", cb->abbr);
  865.     temp = fopen(cb->temp, "r");
  866.     fseek(temp, 0, SEEK_END);
  867.     if (item_len = ftell(temp), item_len > 0)
  868.     {
  869.       out = fopen(buffer, "a");
  870.       fprintf(out, "#! rnews %d\n", (int) item_len);
  871.       fseek(temp, 0, SEEK_SET);
  872.       while (fgets(line, NNTPMAXLEN, temp))
  873.         fputs(line, out);
  874.       fclose(out);
  875.     }
  876.     fclose(temp);
  877.     remove(cb->temp);
  878.     free(cb->temp);
  879.     cb->temp = NULL;
  880.   }
  881.   if (cb->batch > 0)
  882.   {
  883.     (cb->batch)--;
  884.   }
  885. }
  886.  
  887. static void nntp_post(register struct nntpservers *cb)
  888. {
  889.   char buffer[256], line[84];
  890.   FILE *temp;
  891.   
  892.   if (nntptrace > 1)
  893.     cwprintf(cb->window, "NNTP - sending article %s\r\n", cb->topost->name );
  894.   sprintf( buffer, "%s.articles.%s", newsqueue, cb->topost->name );
  895.   if (temp = fopen(buffer, "r"), temp == NULL)
  896.   {
  897.     if (nntptrace > 1)
  898.       cwprintf(cb->window, "NNTP - strange, article file rejects to be opened\r\n" );
  899.     sendit(cb, ".\r\n");
  900.     return;
  901.   }
  902.   while (fgets(line, 81, temp))
  903.   {
  904.     rip(line);
  905.     if (line[0] == '.')
  906.     sendit(cb, ".");
  907.     sendit(cb, "%s\r\n", line);
  908.   }
  909.   sendit(cb, ".\r\n");
  910.   fclose(temp);
  911.   remove(buffer);
  912. }
  913.  
  914. void nntp_transaction(register struct nntpservers *cb)
  915. {
  916.   if (nntptrace > 4) 
  917.     cwprintf(cb->window, "nntp_transaction() enter state=%u stage=%u\r\n", cb->state, cb->stage);
  918.   if (nntptrace > 3)
  919.   {
  920.     cwprintf(cb->window, "%s\r\n",cb->buf);
  921.   }
  922.   if (cb->state == NN_DAT)
  923.   {
  924.     tcp_output(cb->tcb);    /* Send ACK; disk I/O is slow */
  925.  
  926.     if (cb->buf[0] == '.' && cb->buf[1] == '\n' && cb->buf[2] == '\0')
  927.     {
  928.       cb->state = NN_RDY;
  929.       if (cb->output != NULL)
  930.       {
  931.         fclose(cb->output);
  932.         cb->output = NULL;
  933.       }
  934.       switch(cb->stage)
  935.       {
  936.       case NN_NEWG:
  937.         cb->stage = NN_NEWN;
  938.         break;
  939.         
  940.       case NN_NEWN:
  941.         cb->stage = NN_GETN;
  942.         cb->batch = 0;
  943.         break;
  944.         
  945.       case NN_GETN:
  946.       case NN_GNUM:
  947.         save_news(cb);
  948.         break;
  949.         
  950.       case NN_SELG:
  951.         cb->stage = NN_SELG;
  952.         break;
  953.       }
  954.     }
  955.     else
  956.     {
  957.       BOOL dots;
  958.       
  959.       /* Append to data file */
  960.       dots = (strncmp(cb->buf, "..", 2) == 0);
  961.       if (cb->output != NULL)
  962.       {
  963.         if (fprintf(cb->output, "%s", &cb->buf[dots]) < 0)
  964.         {
  965.           cb->state = NN_RDY;
  966.           tprintf(cb->tcb, "File write error\n");
  967.         }
  968.       }
  969.     }
  970.   }
  971.   if (cb->state == NN_CMD)
  972.   {
  973.     char buffer[256];
  974.     struct directory *next;
  975.  
  976.     if (nntptrace > 2)
  977.       cwprintf(cb->window, "%s\n",  cb->buf);
  978.     
  979.     switch(cb->stage)
  980.     {
  981.     case NN_OPEN:
  982.       switch(atoi(cb->buf))
  983.       {
  984.       case 200:
  985.         sprintf(buffer, "%s.articles", newsqueue);
  986.         if (cb->topost = make_dir(buffer), cb->topost != NULL)
  987.           cb->stage = NN_POST;
  988.         else
  989.           cb->stage = NN_NEWG;
  990.         cb->state = NN_RDY;
  991.         break;
  992.         
  993.       case 201:
  994.         cb->stage = NN_NEWG;
  995.         cb->state = NN_RDY;
  996.         break;
  997.         
  998.       default:
  999.         cb->stage = NN_QUIT;
  1000.         cb->state = NN_RDY;
  1001.         break;
  1002.       }
  1003.       break;
  1004.       
  1005.     case NN_POST:
  1006.       switch(atoi(cb->buf))
  1007.       {
  1008.       case 240:
  1009.         sprintf(buffer, "%s.badmarker.%s", newsqueue, cb->topost->name);
  1010.         remove(buffer);
  1011.  
  1012.         next = cb->topost->next;
  1013.         free(cb->topost->name);
  1014.         free(cb->topost);
  1015.         cb->topost = next;
  1016.         if (cb->topost != NULL)
  1017.           cb->stage = NN_POST;
  1018.         else
  1019.           cb->stage = NN_NEWG;
  1020.         cb->state = NN_RDY;
  1021.         break;
  1022.         
  1023.       case 340:
  1024.         nntp_post(cb);
  1025.         cb->stage = NN_POST;
  1026.         cb->state = NN_CMD;
  1027.         break;
  1028.         
  1029.       default:
  1030.         kill_dir(cb->topost);
  1031.         cb->stage = NN_NEWG;
  1032.         cb->state = NN_RDY;
  1033.         break;
  1034.       }
  1035.       break;
  1036.       
  1037.     case NN_NEWG:
  1038.       switch(atoi(cb->buf))
  1039.       {
  1040.       case 231:
  1041.         if (cb->output)
  1042.           fclose(cb->output);
  1043.         sprintf(buffer, "<Mail$Dir>.Folder.%sNG", cb->abbr);
  1044.         if (cb->output = fopen(buffer, "a"), cb->output != NULL)
  1045.         {
  1046.           cb->stage = NN_NEWG;
  1047.           cb->state = NN_DAT;
  1048.         }
  1049.         else
  1050.         {
  1051.           cb->stage = NN_NEWN;
  1052.           cb->state = NN_RDY;
  1053.         }
  1054.         break;
  1055.         
  1056.       default:
  1057.         cb->stage = NN_NEWN;
  1058.         cb->state = NN_RDY;
  1059.         break;
  1060.       }
  1061.       break;
  1062.       
  1063.     case NN_NEWN:
  1064.       switch(atoi(cb->buf))
  1065.       {
  1066.       case 230:
  1067.         if (cb->output)
  1068.           fclose(cb->output);
  1069.         sprintf(buffer, "<NNTP$Dir>.%sNew", cb->abbr);
  1070.         if (cb->output = fopen(buffer, "w"), cb->output != NULL)
  1071.         {
  1072.           cb->stage = NN_NEWN;
  1073.           cb->state = NN_DAT;
  1074.         }
  1075.         else
  1076.         {
  1077.           cb->stage = NN_BYNUM;
  1078.           cb->state = NN_RDY;
  1079.         }
  1080.         break;
  1081.       }
  1082.       break;
  1083.         
  1084.     case NN_GETN:
  1085.     case NN_GNUM:
  1086.       switch(atoi(cb->buf))
  1087.       {
  1088.       case 220:
  1089.         if (cb->output)
  1090.           fclose(cb->output);
  1091.         if (cb->temp)
  1092.         {
  1093.           free(cb->temp);
  1094.           cb->temp = NULL;
  1095.         }
  1096.         if (direct)
  1097.         {
  1098.           char buffer[256];
  1099.           
  1100.           sprintf(buffer, "<Mail$Dir>.Folder.%sNews", cb->abbr);
  1101.           if (cb->output = fopen(buffer, "a"), cb->output != NULL)
  1102.           {
  1103.             cb->state = NN_DAT;
  1104.             fseek(cb->output, 0, SEEK_END);
  1105.             cb->rnewspos = ftell(cb->output);
  1106.             fprintf(cb->output, "#! rnews %07d\n", 0);
  1107.           }
  1108.           else
  1109.           {
  1110.             cb->state = NN_RDY;
  1111.             cb->batch--;
  1112.           }
  1113.         }
  1114.         else
  1115.         {
  1116.           cb->temp = strdup(tmpnam(NULL));
  1117.           if (cb->output = fopen(cb->temp, "w"), cb->output != NULL)
  1118.           {
  1119.             cb->state = NN_DAT;
  1120.           }
  1121.           else
  1122.           {
  1123.             cb->state = NN_RDY;
  1124.             cb->batch--;
  1125.           }
  1126.         }
  1127.         break;
  1128.  
  1129.       case 430:
  1130.       case 423:
  1131.         cb->state = NN_RDY;
  1132.         cb->batch--;
  1133.         break;
  1134.         
  1135.       default:
  1136.         if (cb->stage == NN_GETN)
  1137.           cb->stage = NN_SELG;
  1138.         else
  1139.           cb->stage = NN_QUIT;
  1140.         cb->state = NN_RDY;
  1141.         break;
  1142.       }
  1143.       break;
  1144.       
  1145.     case NN_SELG:
  1146.       switch(atoi(cb->buf))
  1147.       {
  1148.       case 211:
  1149.         cb->stage = NN_SELG;
  1150.         cb->state = NN_RDY;
  1151.         break;
  1152.         
  1153.       default:
  1154.         cb->stage = NN_SELG;
  1155.         cb->state = NN_RDY;
  1156.         break;
  1157.       }
  1158.       break;
  1159.     }
  1160.   }
  1161.   if (cb->state == NN_RDY)
  1162.   {
  1163.     while (!nntp_next_command(cb))
  1164.       ;
  1165.   }
  1166. }
  1167.  
  1168. static void close_down(struct nntpservers *cb, BOOL normal)
  1169. {
  1170.   if (cb->input1   != NULL)
  1171.     fclose(cb->input1);
  1172.   if (cb->input0   != NULL)
  1173.     fclose(cb->input0);
  1174.   if (cb->output   != NULL)
  1175.     fclose(cb->output);
  1176.   if (cb->id_file != NULL)
  1177.     fclose(cb->id_file);
  1178.  
  1179.   if (cb->temp != NULL)
  1180.   {
  1181.   /*  remove(cb->temp); */
  1182.     free(cb->temp);
  1183.     cb->temp = NULL;
  1184.   }
  1185.  
  1186.   cb->input1  = NULL;
  1187.   cb->input0  = NULL;
  1188.   cb->output  = NULL;
  1189.   cb->id_file = NULL;
  1190.  
  1191.   if (cb->window)
  1192.   {
  1193.     cb->window->Attr = ATTR_REVERSE;
  1194.     cb->window->Flags.flags.dont_destroy = FALSE;
  1195.     if (normal)
  1196.       Window_Close(cb->window);
  1197.     else
  1198.       cwprintf(cb->window, "\nThis session has finished, please close the window\n");
  1199.     cb->window = NULL;
  1200.   }
  1201.   start_timer(&cb->nntpcli_t);
  1202. }
  1203.  
  1204. /* close down link after a failure */
  1205. static void quit(struct nntpservers *cb, BOOL normal)
  1206. {
  1207.   cb->state = NN_RDY;
  1208.   sendit(cb, quitcmd);            /* issue a quit command */
  1209.   close_tcp(cb->tcb);             /* close up connection */
  1210.  
  1211.   if (nntptrace)
  1212.   {
  1213.     cwprintf(cb->window, "NNTP Closing down (%s)\r\n", normal ? "Normal" : "Error");
  1214.     if (!normal && cb->buf && cb->buf[0])
  1215.       cwprintf(cb->window, "NNTP Error - %s\r\n", cb->buf);
  1216.   }
  1217.   close_down(cb, normal);
  1218.  
  1219. }
  1220.  
  1221. /* nntp receiver upcall routine.  fires up the state machine to parse input */
  1222. static void nntp_rec(struct tcb *tcb, int16 cnt)
  1223. {
  1224.   register struct nntpservers *cb;
  1225.   char c;
  1226.   struct mbuf *bp;
  1227.  
  1228.   if ((cb = (struct nntpservers *) tcb->user) == NULL) /* point to our struct */
  1229.   {
  1230.     close_tcp(tcb);
  1231.     return;
  1232.   }
  1233.  
  1234.   if (nntptrace > 4)
  1235.     cwprintf(cb->window, "nntp_rec called\r\n");
  1236.  
  1237.   recv_tcp(tcb, &bp, cnt);  /* suck up chars from low level routine */
  1238.  
  1239.   /* Assemble input line in buffer, return if incomplete */
  1240.   while(pullone(&bp,&c) == 1)
  1241.   {
  1242.     switch(c)
  1243.     {
  1244.     case '\r':      /* strip cr's */
  1245.       continue;
  1246.     case '\n':      /* line is finished, go do it! */
  1247.       cb->buf[cb->cnt++] = '\n';
  1248.       cb->buf[cb->cnt] = '\0';
  1249.       nntp_transaction(cb);
  1250.       cb->cnt = 0;
  1251.       break;
  1252.     default:        /* other chars get added to buffer */
  1253.       cb->buf[cb->cnt++] = c;
  1254.       if(cb->cnt > LINELEN - 2)
  1255.       {
  1256.         cb->buf[cb->cnt] = '\0';
  1257.         nntp_transaction(cb);
  1258.         cb->cnt = 0;
  1259.       }
  1260.       break;
  1261.     }
  1262.   }
  1263. }
  1264.  
  1265. /* nntp transmitter ready upcall routine.  twiddles cts flag */
  1266. static void nntp_cts(struct tcb *tcb, int16 cnt)
  1267. {
  1268.   register struct nntpservers *cb;
  1269.  
  1270.   cb = (struct nntpservers *)tcb->user;       /* point to our struct */
  1271.   if (nntptrace > 4)
  1272.   {
  1273.     cwprintf(cb->window, "nntp_cts called avail %d\r\n", cnt);
  1274.   }
  1275.  
  1276.   /* don't do anything until/unless we're supposed to be sending */
  1277.   if(cb->cts == 0)
  1278.     return;
  1279. }
  1280.  
  1281. /* nntp state change upcall routine. */
  1282. static void nntp_state(register struct tcb *tcb, char old, char new)
  1283. {
  1284.   register struct nntpservers *cb;
  1285.   extern char *tcpstates[];
  1286.  
  1287.   old = old;
  1288.  
  1289.   cb = (struct nntpservers *)tcb->user;
  1290.   if (nntptrace > 4)
  1291.     cwprintf(cb->window, "nntp_state called: %s\r\n",tcpstates[new]);
  1292.  
  1293.   switch(new)
  1294.   {
  1295.   case ESTABLISHED:
  1296.     cb->state = NN_CMD;
  1297.     cb->stage = NN_OPEN;
  1298.     cb->cnt = 0;
  1299.     break;
  1300.  
  1301.   case CLOSE_WAIT:
  1302.     close_tcp(tcb);                 /* shut things down */
  1303.     break;
  1304.  
  1305.   case CLOSED:
  1306.     /* if this close was not done by us ie. a RST */
  1307.     cwprintf(cb->window, "NNTP Closed\r\n");
  1308.     close_down(cb, FALSE);
  1309.     del_tcp(tcb);
  1310.     cb->state = NN_CMD;
  1311.     cb->stage = NN_OPEN;
  1312.     break;
  1313.   }
  1314. }
  1315.  
  1316. /* Send message back to server */
  1317. static void sendit(struct nntpservers *cb, char *fmt, ...)
  1318. {
  1319.   va_list argptr;
  1320.   struct mbuf *bp;
  1321.   char tmpstring[256];
  1322.  
  1323.   va_start(argptr,fmt);
  1324.   vsprintf(tmpstring,fmt,argptr);
  1325.   va_end(argptr);
  1326.  
  1327.   if (nntptrace > 3)
  1328.   {
  1329.     cwprintf(cb->window, ">>> %s\r\n", tmpstring);
  1330.   }
  1331.   bp = qdata(tmpstring, strlen(tmpstring));
  1332.   send_tcp(cb->tcb, bp);
  1333. }
  1334.  
  1335. static int dolists(int argc, char *argv[])
  1336. {
  1337.   struct nntpservers *np;
  1338.   
  1339.   for (np = Nntpservers; np != NULLNNTP; np = np->next)
  1340.   {
  1341.     char tbuf[80];
  1342.  
  1343.     if (np->lowtime != -1 && np->hightime != -1)
  1344.             sprintf(tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime/100, np->lowtime%100, np->hightime/100, np->hightime%100);
  1345.     else
  1346.       tbuf[0] = '\0';
  1347.     cwprintf(NULL, "%-32s (%lu/%lu%s)\r\n", np->name,
  1348.             read_timer(&np->nntpcli_t) /1000L,
  1349.             dur_timer(&np->nntpcli_t) /1000L,
  1350.             tbuf);
  1351.   }
  1352.   return 0;
  1353. }
  1354.